import axios from 'axios'
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'
import { ElLoading, ElMessage } from 'element-plus/es'
import { useStorage, useStorageType } from '@/utils'
import { LOGIN_PATH, TOKEN } from '@/constants'
import router from '@/router'

interface InterceptorHooks {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any

  responseInterceptor?: (response: AxiosResponse) => AxiosResponse
  responseInterceptorCatch?: (error: any) => any
}

interface RequestConfig extends AxiosRequestConfig {
  hideLoading?: boolean
  interceptorHooks?: InterceptorHooks
}

interface RequestData<T> {
  data: T
  returnCode: string
  success: boolean
}

class Request {
  public config: AxiosRequestConfig
  public interceptorHooks?: InterceptorHooks
  public hideLoading: boolean
  public loading?: any
  public instance: AxiosInstance

  constructor(options: RequestConfig) {
    this.config = options
    this.interceptorHooks = options.interceptorHooks
    this.hideLoading = options.hideLoading ?? false
    this.instance = axios.create(options)

    this.setupInterceptor()
  }

  public setupInterceptor(): void {
    this.instance.interceptors.request.use(
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch
    )
    this.instance.interceptors.response.use(
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.responseInterceptorCatch
    )

    this.instance.interceptors.request.use((config) => {
      if (!this.hideLoading) {
        this.loading = ElLoading.service({
          lock: true,
          text: '加载中...',
          background: 'rgba(0, 0, 0, 0.7)'
        })
      }
      return config
    })

    this.instance.interceptors.response.use(
      (res) => {
        this.loading?.close()
        return res
      },
      (err) => {
        this.loading?.close()
        return err
      }
    )
  }

  public request<T = any>(config: RequestConfig): Promise<T> {
    if (config.hideLoading) {
      this.hideLoading = true
    }
    return new Promise((resolve, reject) => {
      // 防止出现多次请求与提示信息
      const token = useStorage.getItem(TOKEN, useStorageType.Cookies)
      const currentPath = router.currentRoute.value.path
      if (!token && currentPath !== LOGIN_PATH) return
      // 实例请求数据
      this.instance
        .request<any, RequestData<T>>(config)
        .then((res: any) => {
          // resolve(res.data)
          switch (res.status || res.code) {
            case 1 || 200: // 成功
              if (res.msg) {
                ElMessage.success(res.msg)
              }
              resolve(res)
              break
            case 401: // 未授权 needLogin
              if (res.msg) {
                ElMessage.error(res.msg)
              }
              // 删除token 并跳转登录页
              useStorage.removeItem(TOKEN, useStorageType.Cookies)
              router.replace(LOGIN_PATH).then((r) => r)
              break
            default:
              if (res.msg) {
                ElMessage.error(res.msg)
              }
          }
        })
        .catch((err) => {
          reject(err)
        })
        .finally(() => {
          this.hideLoading = false
        })
    })
  }

  public get<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: 'GET' })
  }

  public post<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: 'POST' })
  }

  public put<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PUT' })
  }

  public delete<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE' })
  }

  public patch<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PATCH' })
  }
}

export default Request
